package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.apis.article.ArticleFeignClient;
import com.heima.apis.schedule.TaskFeignClient;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.enums.TaskTypeEnum;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.ProtostuffUtil;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsCheckService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class WmNewsCheckServiceImpl implements WmNewsCheckService {
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private ArticleFeignClient articleFeignClient;
    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private Tess4jClient tess4jClient;
    @Autowired
    private TaskFeignClient taskFeignClient;

    /**
     * 自媒体文章发布时审核
     * @param newsId
     */
    @Override
    public void checkNews(Integer newsId) {
        //1.参数校验:通过id查询文章是否村存在
        WmNews wmNews = wmNewsMapper.selectById(newsId);
        if (wmNews == null){
            throw new RuntimeException("文章数据不存在，无法审核");
        }
        //2.抽取文章文本内内容和图片内容(封装称方法)
        Map<String, Object> result = extractTextAndImages(wmNews);

        //3.内容审核
        //3.1文本内容审核
        String text = result.get("text").toString();
        //需求增加：自管理敏感词对文本审核
        Boolean textSensitiveResult = checkSensitiveWord(wmNews,text);
        if (!textSensitiveResult){
            return;
        }
        //阿里云接口文本内容审核
        Boolean textAliyunResult = checkText(wmNews,text);
        if (!textAliyunResult){
            return;
        }
        //3.2图片内容审核
        //需求增加：对团片中的敏感词机进行自管理审核，在审核图片的方法中加入敏感词审核
        //阿里云图片审核
        List<String> images = (List<String>) result.get("images");
        Boolean imageResult = checkImages(wmNews,images);
        if (!imageResult){
            return;
        }
        //4.审核通过发布文章(远程调用发布文章)
        //4.1自媒体发布文章后，将文章保存到端
//         ResponseResult<Long> responseResult = saveWmNewsToArticle(wmNews);
//        //4.2远程调用发布文章和添加降级处理
          //调用失败降级处理
//        if (responseResult.getCode()!=200){
//            throw new RuntimeException("远程调用文章服务异常："+responseResult.getErrorMessage());
//        }
//        //调用成功，修改数据
//        wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
//        wmNews.setReason("文章发布成功！");
//        wmNewsMapper.updateById(wmNews);
        //定时任务发布文章需求：定时任务发布文章时，需要先对文章审核好，到发布时间后直接发布，则需要在审核功能中添加定时发布任务
        //4.2定时发布任务
        //4.2.1修改自媒体文章状态
        wmNews.setStatus(WmNews.Status.SUCCESS.getCode());
        wmNews.setReason("文章审核通过！");
        wmNewsMapper.updateById(wmNews);
        //4.2.2定时发布文章
        wmNewsTimingPublish(wmNews.getId(),wmNews.getPublishTime());
    }



    //==============================================收取出来的方法====================================

    /**
     * 定时发布文章的方法
     * @param newsId
     * @param publishTime
     */
    private void wmNewsTimingPublish(Integer newsId,Date publishTime) {
        //1.创建对象，封装数据
        Task task = new Task();
        //1.1封装任务发布时间
        task.setExecuteTime(publishTime.getTime());
        //1.2封装类型和优先级
        task.setTaskType(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType());
        task.setPriority(TaskTypeEnum.NEWS_SCAN_TIME.getPriority());
        //1.3封装任务中需要发布的文章内容,setParameters需要的参数是数组，需要对对象序列化后存储
        //1.3.1创建文章对象，封装数据（为了方便序列化，只用封装文章的id即可）
        WmNews wmNews = new WmNews();
        wmNews.setId(newsId);
        //2.将wmNews序列化后转为字节数组，调用工具类中的方法
        byte[] bytes = ProtostuffUtil.serialize(wmNews);
        task.setParameters(bytes);

        //3.调用远程接口任务服务，发布定时任务
        ResponseResult responseResult = taskFeignClient.addTask(task);
        if (responseResult.getCode()!=200){
            throw new RuntimeException("文章定时发布任务失败");
        }

    }


    /**
     * 将文章内容保存到App端Article中的方法
     * @param wmNews
     * @return
     */
    @Override
    public ResponseResult<Long> saveWmNewsToArticle(WmNews wmNews) {
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews,articleDto);
        //4.2处理字段不一致无法复制上的数据：id() authorId authorName channelName layout
        //id：可能有可能没有：文章第一次发布审核时，此时时没有id的，若是对审核后文章修改后重新发布，此时文章就存在di，因为审核完成后会返回一个id
        if (wmNews.getArticleId()!=null){
            articleDto.setId(wmNews.getArticleId());
        }
        //authorId
        articleDto.setAuthorId(wmNews.getUserId().longValue());
        //authorName
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null){
            articleDto.setAuthorName(wmUser.getName());
        }
        //channelName
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (wmChannel!= null){
            articleDto.setChannelName(wmChannel.getName());
        }
        //layout
        articleDto.setLayout(wmNews.getType());

        ResponseResult<Long> responseResult = articleFeignClient.saveArticle(articleDto);
        return responseResult;
    }



    /**
     * 自管理敏感词文本审核
     * @param wmNews
     * @param text
     * @return
     */
    private Boolean checkSensitiveWord(WmNews wmNews, String text) {
        //1.参数校验
        if (StringUtils.isBlank(text)){
            return true;
        }
        //2.敏感词审核
        Map<String, Integer> words = SensitiveWordUtil.matchWords(text);
        if (words.size()>0){
            //存在敏感词
            wmNews.setStatus(WmNews.Status.FAIL.getCode());
            wmNews.setReason("文本内容或图片中的文本存在敏感词信息！");
            wmNewsMapper.updateById(wmNews);
            return false;
        }
        return true;
    }

    /**
     * 阿里云图片审核的方法
     * @param wmNews
     * @param images
     * @return
     */
    private Boolean checkImages(WmNews wmNews, List<String> images) {
        //1.判断是否存在图片数据
        if (CollectionUtils.isEmpty(images)){
            return true;
        }
        //2.审核,并修改审核状态
        //2.1下载图片
        List<byte[]> list = images.stream().map(image -> {
            byte[] bytes = fileStorageService.downLoadFile(image);
            return bytes;
        }).collect(Collectors.toList());
        //2.2进行审核
        try {
            //1.先审核图片中的文字是否存在敏感字
            for (byte[] bytes : list) {
                //3.ByteArrayInputStream可以将bytes转换称流
                ByteArrayInputStream is = new ByteArrayInputStream(bytes);
                //2.ImageIO.read可以得到BufferedImage对象，但是需要一个输入流
                BufferedImage bufferedImage = ImageIO.read(is);
                //1.docr中需要的参数类型BufferedImage
                String ocr = tess4jClient.doOCR(bufferedImage);

                //4.得到文字图片中的文本后吗，对文本进行敏感词审核
                Boolean sensitiveWord = checkSensitiveWord(wmNews, ocr);
                if (!sensitiveWord){
                    return false;//此处不在需要跟新文章信息，checkSensitiveWord方法中已经更新了
                }
            }
            //2.阿里云图片审核
            Map map = greenImageScan.imageScan(list);
            if ("block".equals(map.get("suggestion"))){
                //审核失败,修改文章状态,添加审核原因
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("图片存在违规信息！");
                wmNewsMapper.updateById(wmNews);
                return false;
            }
            if ("review".equals(map.get("suggestion"))){
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
                wmNews.setReason("图片存在不确定的信息！");
                wmNewsMapper.updateById(wmNews);
                return false;
            }
            //审核通过
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            wmNews.setReason("阿里云审核异常，审核失败");
            wmNewsMapper.updateById(wmNews);
            return false;
        }
    }


    /**
     * 阿里云文本审核的方法
     * @param wmNews
     * @param text
     * @return
     */
    private Boolean checkText(WmNews wmNews, String text) {
        //1.判断是否村咋爱文本数据
        if (StringUtils.isBlank(text)){
            //直接审核通过
            return true;
        }
        //2.审核,并判断审核状态
        try {
            Map<String,String> map = greenTextScan.greeTextScan(text);
            //审核后的状态：pass通过，block不通过，review人工审核
            if ("block".equals(map.get("suggestion"))){
                //审核失败,修改文章状态,添加审核原因
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("文本内容存在违规信息！");
                //修改数据
                wmNewsMapper.updateById(wmNews);
                return false;
            }
            if ("review".equals(map.get("suggestion"))){
                //需要人工审核
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
                wmNews.setReason("文本内容存在不确定的信息！");
                wmNewsMapper.updateById(wmNews);
                return false;
            }
            //审核通过
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
            wmNews.setReason("阿里云审核异常，审核失败");
            wmNewsMapper.updateById(wmNews);
            return false;
        }
    }

    /**
     * 抽取文章中的文本和图片内容的方法
     * @param wmNews
     * @return
     */
    private Map<String, Object> extractTextAndImages(WmNews wmNews) {
        //0.创建两个对象存储收取的文本和图片
        StringBuilder text = new StringBuilder();
        List<String> images = new ArrayList<>();

        //1.抽取内容中的文本和图片
        //1.1获取文章内容
        String content = wmNews.getContent();
        //1.2文章内容转为java对象，方便获取里面的文本和图片内容（以）
        List<Map> maps = JSON.parseArray(content, Map.class);
        for (Map map : maps) {
             //判断类型是文本还是图片
            //为图片
            if ("image".equals(map.get("type"))) {
                images.add((String) map.get("value"));
            }
            //为文本
            if ("text".equals(map.get("type"))){
                text.append(map.get("value")+",");//加上，号分割两个文本内容拼接时会出现审核不通过的文字
            }
        }

        //2.抽取标题中的文本
        String title = wmNews.getTitle();
        text.append(title+",");

        //3.抽取封面中的图片
        //3.1先判断封面中是否存在图片
        if(StringUtils.isNotBlank(wmNews.getImages())){
            //存在图片
            String[] split = wmNews.getImages().split(",");
            //添加到图片对象中
            images.addAll(Arrays.asList(split));
        }

        //4，返回抽取的结果
        //4.1创建一个集合封装抽取的数据
        Map<String , Object> result = new HashMap<>();
        result.put("text",text);
        result.put("images",images);
        //4.2返回
        return result;
    }
}
